// BHDll.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "biohelper.h"

//
// Flow macros
// 

static 
void
WINAPI
_OutputDbgStr(
    __in  LPSTR szFile,
    __in  DWORD dwLine,
    __in  LPSTR szMsg,
    __in  HRESULT hr)
{
    CHAR rgsz [256];
    LPSTR szTag = "INFO";

    if (FAILED(hr))
        szTag = "ERROR";

    StringCbPrintfA(
        rgsz,
        sizeof(rgsz),
        "%s: %s - 0x%x, file %s, line %d\n",
        szTag,
        szMsg,
        hr,
        szFile,
        dwLine);
    OutputDebugStringA(rgsz);
}

#define CHECK_HR(_X) {                                          \
    if (FAILED(hr = _X)) {                                      \
        _OutputDbgStr(__FILE__, __LINE__, #_X, (DWORD) hr);     \
        __leave;                                                \
    }                                                           \
}

#define CHECK_BOOL(_X) {                                        \
    if (FALSE == _X) {                                          \
        hr = (HRESULT) GetLastError();                          \
        _OutputDbgStr(__FILE__, __LINE__, #_X, (DWORD) hr);     \
        __leave;                                                \
    }                                                           \
}

#define CHECK_ALLOC(_X) {                                       \
    if (NULL == (_X)) {                                         \
        hr = E_OUTOFMEMORY;                                     \
        __leave;                                                \
    }                                                           \
}

#define LOG_CALL(_X, _Y) {                                      \
    _OutputDbgStr(__FILE__, __LINE__, #_X, _Y); }

//
// Heap Helpers
//

void* _Alloc(size_t size)
{
    return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
}

void _Free(void* pv)
{
    HeapFree(GetProcessHeap(), 0, pv);
}

//
// Other helper routines
//

VOID
CALLBACK
_BioIdentifyCallback(
    __in_opt PVOID IdentifyCallbackContext,
    __in HRESULT OperationStatus,
    __in WINBIO_UNIT_ID UnitId,
    __in WINBIO_IDENTITY *Identity,
    __in WINBIO_BIOMETRIC_SUBTYPE SubFactor,
    __in WINBIO_REJECT_DETAIL RejectDetail)
{
    HRESULT hr = S_OK;
    PBIO_HELPER_CONTEXT pCtx = (PBIO_HELPER_CONTEXT) IdentifyCallbackContext;
    HANDLE hToken = NULL;
    DWORD cbInfo = 0;
    PTOKEN_USER pTokenUser = NULL;
    SID_NAME_USE snu;
    DWORD cchUser = 0;
    DWORD cchDomain = 0;

    UNREFERENCED_PARAMETER(UnitId);
    UNREFERENCED_PARAMETER(SubFactor);
    UNREFERENCED_PARAMETER(RejectDetail);

    __try
    {
        if (NULL != pCtx->wszMatchedUser)
        {
            _Free(pCtx->wszMatchedUser);
            pCtx->wszMatchedUser = NULL;
        }
        if (NULL != pCtx->wszMatchedDomain)
        {
            _Free(pCtx->wszMatchedDomain);
            pCtx->wszMatchedUser = NULL;
        }
        pCtx->fMatchesLoggedOnUser = FALSE;
        pCtx->hrIdentify = S_OK;

        //
        // Set the status code of the Identify attempt
        //

        if (FAILED(OperationStatus))
        {
            pCtx->hrIdentify = OperationStatus;
            __leave;
        }

        //
        // Check the match type
        //

        if (WINBIO_ID_TYPE_SID != Identity->Type)
        {
            OutputDebugStringA("Did not receive Bio identity type SID.\n");
            pCtx->hrIdentify = (HRESULT) ERROR_INVALID_PARAMETER;
            __leave;
        }

        //
        // Get the user and domain name of the match
        //

        LookupAccountSidW(
            NULL,
            (PSID) &Identity->Value.AccountSid.Data,
            NULL,
            &cchUser,
            NULL,
            &cchDomain,
            &snu);

        CHECK_ALLOC(
            pCtx->wszMatchedUser = (LPWSTR) _Alloc(sizeof(WCHAR) * cchUser));
        CHECK_ALLOC(
            pCtx->wszMatchedDomain = (LPWSTR) _Alloc(
                sizeof(WCHAR) * cchDomain));

        CHECK_BOOL(LookupAccountSidW(
            NULL,
            (PSID) &Identity->Value.AccountSid.Data,
            pCtx->wszMatchedUser,
            &cchUser,
            pCtx->wszMatchedDomain,
            &cchDomain,
            &snu));

        //
        // Get the current token
        //

        if (FALSE == OpenThreadToken(
            GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
        {
            CHECK_BOOL(OpenProcessToken(
                GetCurrentProcess(), TOKEN_QUERY, &hToken));
        }

        //
        // Get the current SID
        //

        GetTokenInformation(hToken, TokenUser, NULL, 0, &cbInfo);
        CHECK_ALLOC(pTokenUser = (PTOKEN_USER) _Alloc(cbInfo));
        CHECK_BOOL(GetTokenInformation(
            hToken, TokenUser, (LPVOID) pTokenUser, cbInfo, &cbInfo));

        //
        // Check if the logon and match SIDs are the same
        //

        if (TRUE == EqualSid(
                pTokenUser->User.Sid, (PSID) &Identity->Value.AccountSid.Data))
            pCtx->fMatchesLoggedOnUser = TRUE;
    }
    __finally
    {
        if (NULL != pTokenUser)
            _Free(pTokenUser);
        if (NULL != hToken)
            CloseHandle(hToken);
    }

    if (S_OK == pCtx->hrIdentify && FAILED(hr))
        pCtx->hrIdentify = hr;

	//
    // Conditionally execute the callback
    //

	if (NULL != pCtx->pfnCallback)
		pCtx->pfnCallback(pCtx);
}

//
// Export routines
//

HRESULT
WINAPI
BHInitialize(
    __inout         PBIO_HELPER_CONTEXT pCtx)
{
        //
        // Get a session handle
        //

        return WinBioOpenSession(
            WINBIO_TYPE_FINGERPRINT,
            WINBIO_POOL_SYSTEM,
            WINBIO_FLAG_DEFAULT,
            NULL,
            0,
            WINBIO_DB_DEFAULT,
            &pCtx->hSession);
}

void
WINAPI
BHCleanup(
    __inout         PBIO_HELPER_CONTEXT pCtx)
{
    if (NULL != pCtx->wszMatchedUser)
        _Free(pCtx->wszMatchedUser);
    if (NULL != pCtx->wszMatchedDomain)
        _Free(pCtx->wszMatchedDomain);
    if (0 != pCtx->hSession)
        WinBioCloseSession(pCtx->hSession);
}

HRESULT
WINAPI
BHIdentifyAsync(
    __inout         PBIO_HELPER_CONTEXT pCtx)
{
    return WinBioIdentifyWithCallback(
        pCtx->hSession,
        _BioIdentifyCallback,
        (PVOID) pCtx);
}

HRESULT
WINAPI
BHIdentifyAsLoggedOnUser(
	__out			BOOL *pfIsLoggedOnUser,
	__out			ULONG *pulRejectDetail)
{
	HRESULT hr = S_OK;
	BIO_HELPER_CONTEXT Ctx = {0};
	WINBIO_UNIT_ID UnitId = 0;
	WINBIO_IDENTITY Identity = {0};
	WINBIO_BIOMETRIC_SUBTYPE SubType = 0;

	__try
	{
		*pfIsLoggedOnUser = FALSE;

		CHECK_HR(BHInitialize(&Ctx));

		CHECK_HR(WinBioIdentify(
			Ctx.hSession,
			&UnitId,
			&Identity,
			&SubType,
			pulRejectDetail));

		_BioIdentifyCallback(
			&Ctx,
			S_OK,
			UnitId,
			&Identity,
			SubType,
			*pulRejectDetail);

		if (TRUE == Ctx.fMatchesLoggedOnUser)
			*pfIsLoggedOnUser = TRUE;
	}
	__finally
	{
		BHCleanup(&Ctx);
	}

	return hr;
}

HRESULT
WINAPI
BHCancel(
    __inout         PBIO_HELPER_CONTEXT pCtx)
{
    return WinBioCancel(pCtx->hSession);
}

